<?php
/**
 * PEAR Include
 */
require_once 'DB.php';
@require_once 'Log.php'; // Может такого и нет, тогда бум использовать свою заглушку

/**
 * Альтернативный класс Log'ера. 
 * Ничего не делает, просто заглушка, если отсудствует класс Log из PEAR.
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 *
 */
class phpICQ_log
{
    function open()
    {
        return false;
    }
    function close()
    {
        return false;
    }
    function flush()
    {
        return false;
    }
    function log($message, $priority = null)
    {
        sprintf('%s%s',$message,$priority);
        return false;
    }
    function emerg($message)
    {
        return $this->log($message, 0);
    }
    function alert($message)
    {
        return $this->log($message, 1);
    }
    function crit($message)
    {
        return $this->log($message, 2);
    }
    function err($message)
    {
        return $this->log($message, 3);
    }
    function warning($message)
    {
        return $this->log($message, 4);
    }
    function notice($message)
    {
        return $this->log($message, 5);
    }
    function info($message)
    {
        return $this->log($message, 6);
    }
    function debug($message)
    {
        return $this->log($message, 7);
    }
}

/**
 * Класс работы с БД.
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 *
 */
class phpICQ_baseConnection
{
    /**
     * @var DB_mysql
     */
    public $db;
    /**
     * @var string
     */
    private $dsn;
    /**
     * @var array
     */
    private $options;
    /**
     * @var int
     */
    private $querysCount = 0;
    /**
     * @var Log
     */
    private $logger;
    /**
     * @var string
     */
    private $logStr;
    /**
     * @var bool
     */
    private $logAllQuerys;

    /**
     * Конструктор класа
     *
     * @param string $dsn
     * @param array $options
     */
    function __construct ($dsn, $options = null, $logStr = "", $logAllQuerys = false)
    {
        if (is_array($options)) {
            $this->options = $options;
        } else {
            $this->options = array("debug" => 6, "persistent" => false);
        }
        
        $this->logAllQuerys = $logAllQuerys;
        $this->dsn          = $dsn;
        $this->db           = null;
        $this->ConnectToBase();
        
        $this->logStr = $logStr;
    }

    /**
     * @return string
     */
    public function getDSN()
    {
        return $this->dsn;
    }
    
    private function ConnectToBase ()
    {
        if ($this->db != null) {
            $this->db->disconnect();
        }
        $this->db = &DB::connect($this->dsn, $this->options);
        
        if (DB::isError($this->db)) {
            die($this->logStr.": ".$this->db->getMessage()."\n");
        }
        
        $this->db->setFetchMode(DB_FETCHMODE_ASSOC);
        $this->logger = $this->GetLoger('sql');
    }

    /**
     * Возвращает объект логера
     * 
     * @param string $ident
     * @return Log
     */
    public function GetLoger ($ident = "def")
    {
        if (class_exists('Log')) {
            $logger = &Log::singleton('sql', 'log_table', $ident, array('db' => $this->db));
            if ($logger == null) {
                $logger = new Log();
            }
        } else {
            $logger = new phpICQ_log();
        }
        return $logger;
    }

    /**
     * @param string $query_str
     * @return DB_result
     */
    public function query ($query_str)
    {
        // TODO А если мы отвалились от базы... че-ж делать?  (mysql_ping)
        $res = & $this->db->query($query_str);
        
        // Always check that result is not an error
        if (PEAR::isError($res)) {
            @$this->logger->err($query_str);
            @$this->logger->err($res->getMessage());
            die($this->logStr.": ".$res->getMessage()."\n".$query_str."\n");
        }
        
        $this->querysCount = $this->querysCount + 1;
        
        if ($this->logAllQuerys) {
            $this->logger->log($query_str);  
        }
        return $res;
    }

    /**
     * @return int
     */
    public function affectedRows ()
    {
        return $this->db->affectedRows();
    }

    /**
     * Массив полей таблицы
     * 
     * @return array
     */
    public function GetTableFieldsNames ($TableName)
    {
        $result = $this->query('SHOW COLUMNS FROM `' . $TableName . '`');
        $fieldnames = array();
        if (! $result) {
            return $fieldnames;
        }
        
        while (($row = $result->fetchRow()) == true) {
            $fieldnames[] = $row['Field'];
        }
        
        return $fieldnames;
    }

    /**
     * @return int
     */
    public function GetQuerysCount ()
    {
        return $this->querysCount;
    }
    
    /**
     * Отключение от базы
     *
     * @return bool
     */
    public function disconnect()
    {
        return $this->db->disconnect(); 
    }
}

/**
 * Список используемых протоколов
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 * 
 */
class phpICQ_protocol
{
    const icq  = 1;
    const mrim = 2;
}

/**
 * Класс описания полей в SQL базе
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 * 
 */
class phpICQ_SQLFieldsNames
{
    // Поля контакта
    const contact_tablename = "contactlist";
    const contact_userid_fieldname = "userid";
    const contact_protocol_fieldname = "protocol";
    const contact_id_fieldname = "id";
    const contact_serverid_fieldname = "serverid";
    const contact_groupid_fieldname = "groupid";
    const contact_uin_fieldname = "uin";
    const contact_name_fieldname = "name";
    const contact_displayname_fieldname = "displayname";
    const contact_statustext_fieldname = "statustext";
    const contact_status_fieldname = "status";
    const contact_xstatus_fieldname = "xstatus";
    const contact_usercharset_fieldname = "usercharset";
    const contact_incontactlist_fieldname = "incontactlist";
    const contact_notaspam_fieldname = "notaspam";
    const contact_changed_fieldname = "changed";
    const contact_deleted_fieldname = "deleted";
    
    // Поля и таблица профайлов юзеров
    const usersprofiles_tablename = "users";
    const usersprofiles_id_fieldname = "id";
    const usersprofiles_uin_fieldname = "uin";
    const usersprofiles_pass_fieldname = "pass";
    const usersprofiles_name_fieldname = "name";
    const usersprofiles_userpass_fieldname = "userpass";
    const usersprofiles_statustext_fieldname = "statustext";
    const usersprofiles_status_fieldname = "status";
    const usersprofiles_xstatus_fieldname = "xstatus";
    
    // Поля таблицы сообщений
    const messages_tablename = "messages";
    const messages_userid_fieldname = "userid";
    const messages_id_fieldname = "id";
    const messages_serverid_fieldname = "serverid";
    const messages_contactuin_fieldname = "uin";
    const messages_issend_fieldname = "issend";
    const messages_isdelivered_fieldname = "isdelivered";
    const messages_message_fieldname = "message";
    const messages_date_fieldname = "date";
    const messages_inout_fieldname = "inout";
    const messages_sendtime_fieldname = "sendtime";
    const messages_attempts_fieldname = "attempts";
    
    // Поля таблицы настроек
    const settings_tablename = "settings";
    const settings_userid_fieldname = "userid";
    const settings_fields_fieldname = "field";
    const settings_values_fieldname = "value";
    
    // Поля таблицы статистики
    const statistics_tablename = "statistics";
    const statistics_userid_fieldname = "userid";
    const statistics_in_fieldname = "in";
    const statistics_out_fieldname = "out";
    const statistics_requests_fieldname = "requests";
    const statistics_querys_fieldname = "querys";
    const statistics_lastactivity_fieldname = "lastactivity";
    
    // Поля таблицы смайликов
    const smiles_tablename = "smiles";
    const smiles_smile_fieldname = "str";
    const smiles_filename_fieldname = "filename";
    
    // Поля таблицы антиспам-фильтра
    const antspam_tablename = "antispam_questions";
    const antspam_id_fieldname = "id";
    const antspam_userid_fieldname = "userid";
    const antspam_question_fieldname = "question";
    const antspam_answer_fieldname = "answer";
    const antspam_invitation_fieldname = "invitation";
    
    // Поля таблицы статусов
    const statuses_tablename = "statuses";
    const statuses_id_fieldname = "id";
    const statuses_protocol_fieldname = "protocol";
    const statuses_name_fieldname = "name";
    const statuses_alias_fieldname = "alias";
    const statuses_picture_fieldname = "picture";
    const statuses_isonline_fieldname = "isonline";
    const statuses_showasuserstatus_fieldname = "showasuserstatus";

    //***************************
    static function GetUserProfileFields ()
    {
        return array(phpICQ_SQLFieldsNames::usersprofiles_id_fieldname, 
                    phpICQ_SQLFieldsNames::usersprofiles_uin_fieldname, 
                    phpICQ_SQLFieldsNames::usersprofiles_pass_fieldname, 
                    phpICQ_SQLFieldsNames::usersprofiles_name_fieldname, 
                    phpICQ_SQLFieldsNames::usersprofiles_statustext_fieldname, 
                    phpICQ_SQLFieldsNames::usersprofiles_status_fieldname, 
                    phpICQ_SQLFieldsNames::usersprofiles_xstatus_fieldname, 
                    phpICQ_SQLFieldsNames::usersprofiles_userpass_fieldname);
    }

    static function GetContactFields ()
    {
        return array(phpICQ_SQLFieldsNames::contact_userid_fieldname, 
                    phpICQ_SQLFieldsNames::contact_id_fieldname,
                    phpICQ_SQLFieldsNames::contact_protocol_fieldname, 
                    phpICQ_SQLFieldsNames::contact_serverid_fieldname, 
                    phpICQ_SQLFieldsNames::contact_groupid_fieldname, 
                    phpICQ_SQLFieldsNames::contact_uin_fieldname, 
                    phpICQ_SQLFieldsNames::contact_name_fieldname, 
                    phpICQ_SQLFieldsNames::contact_displayname_fieldname, 
                    phpICQ_SQLFieldsNames::contact_statustext_fieldname, 
                    phpICQ_SQLFieldsNames::contact_status_fieldname, 
                    phpICQ_SQLFieldsNames::contact_xstatus_fieldname, 
                    phpICQ_SQLFieldsNames::contact_usercharset_fieldname, 
                    phpICQ_SQLFieldsNames::contact_incontactlist_fieldname, 
                    phpICQ_SQLFieldsNames::contact_notaspam_fieldname, 
                    phpICQ_SQLFieldsNames::contact_changed_fieldname, 
                    phpICQ_SQLFieldsNames::contact_deleted_fieldname);
    }
}

/**
 * ICQ - статусы
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 *
 */
class phpICQ_statuses
{
    /**
     * @var phpICQ_userProfile
     */
    private $UserProfile;
    /**
     * @var array
     */
    private $statuses;
	/**
     * @var array
     */
    private $x_statuses = array("journal", 	"angry", 	"ppc", 		"cinema", 	"phone", 	"browsing", "mobile", 	"wc",	"coffee", 
    							"sick", 	"picnic",	"thinking", "smoking", 	"business", "duck", 	"studying", "?", 	"typing", 	
    							"shopping", "music", 	"zzz", 		"fun", 		"sleeping", "tv", 		"tired",	"beer", "surfing", 
    							"pro7", 	"working", 	"love2", 	"gaming", 	"google", 	"love", 	"party", 	"sex", 	"meeting", 
    							"eating");

    /*
     * 
     */
    function __construct($UserProfile)
    {
        $this->UserProfile = $UserProfile;

        //$bl = $result = $this->UserProfile->GetBaseLink();
        $result = $this->UserProfile->GetBaseLink()->query('SELECT * FROM `'.phpICQ_SQLFieldsNames::statuses_tablename.'`');

        //DB_FETCHMODE_ASSOC
        //DB_FETCHMODE_OBJECT
        $this->statuses = array();
        while (($row = $result->fetchRow()) == true) {
            $this->statuses[] = $row; 
        }
    }

    /**
     * @return  array
     */
    static function GetXStatuses ()
    {
        return $this->x_statuses;
    }

    /**
     * @return  array
     */
    public function GetAllStatuses()
    {
        return $this->statuses;
    }

    /**
     * @return boolean
     */
    static function IsStatusCorrect ($status)
    {
        return (array_search($status, phpICQ_statuses::GetAllStatuses()) !== false);
    }

}

/**
 * Класс работы со смайликами
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 * 
 */
class phpICQ_smiles
{
    
    /**
     * @var array
     */
    private $smiles;
    /**
     * @var array
     */
    private $smilesFiles;
    /**
     * @var phpICQ_userProfile
     */
    private $userProfile;
    
    private $smilesFolder = "images/smiles/";

    /**
     * Конструктор класса
     *
     * @param phpICQ_userProfile $userProfile
     */
    function __construct ($userProfile)
    {
        $this->userProfile = $userProfile;
        $this->getSmilesFromBase();
    }

    /**
     * Каталог содержащий смайлики
     * 
     * @return str
     */
    public function GetSmilesFolder ()
    {
        return $this->smilesFolder;
    }

    /**
     * Читает все смайлики из базы
     */
    public function getSmilesFromBase ()
    {
        $result = $this->userProfile->GetBaseLink()->query('SELECT * FROM `' . phpICQ_SQLFieldsNames::smiles_tablename .
         '`');
        $this->smiles = Array();
        
        while (($row = $result->fetchRow()) == true) {
            $this->smiles[] = $row[phpICQ_SQLFieldsNames::smiles_smile_fieldname];
            $this->smilesFiles[] = $row[phpICQ_SQLFieldsNames::smiles_filename_fieldname];
        }
    }

    /**
     * Возвращает псевдокод картинки смайлика
     * 
     * @return str
     */
    private function makeSmile ($i, $smileText)
    {
        return '[smile_image src="' . $this->smilesFolder . $this->smilesFiles[$i] . '" txt="'.$smileText.'"]';
    }

    /**
     * заменяет смайлики на псевдо коды картинок со смайликом
     * 
     * @param str $strIn
     * @return str
     */
    public function replaceSmiles ($strIn)
    {
        $i = 0;
        $str = $strIn;
        foreach ($this->smiles as $smile) {
            $currentSmiles = explode(",", $smile);
            foreach ($currentSmiles as $currentSmile) {
                $str = str_replace($currentSmile, $this->makeSmile($i, $currentSmile), $str);
            }
            $i ++;
        }
        return $str;
    }

    /**
     * Возвращает массив смайликов для пользователя (уресанный полный массив т.е. 1 смайлику 1 картинка)
     * 
     * @return array
     * 
     */
    public function GetSmilesForUser ()
    {
        $i = 0;
        $smiles = Array();
        foreach ($this->smiles as $smile) {
            $currentSmiles = explode(",", $smile);
            foreach ($currentSmiles as $currentSmile) {
                $smiles[] = (object) array("smile" => $currentSmile, 
                "file" => $this->smilesFiles[$i]);
                break;
            }
            $i ++;
        }
        return $smiles;
    }

} // phpICQ_smiles



/**
 * Класс вопросов для антиспам фильта
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 * 
 */
class phpICQ_antiSpamFilter
{
    /**
     * @var phpICQ_userProfile
     */
    private $UserProfile;
    /**
     * @var array 
     */
    private $questiosList;
    /**
     * @var bool
     */
    private $useAntiSpamFilter;

    /**
     * Конструктор класса
     *
     * @param phpICQ_userProfile $UserProfile
     */
    function __construct ($UserProfile)
    {
        $this->UserProfile = $UserProfile;
        $this->useAntiSpamFilter = $this->UserProfile->useAntiSpamFilter();
        if ($this->useAntiSpamFilter) {
            $this->GetQuestionsListBromBase();
            if (count($this->questiosList) == 0) {
                $this->SetDefaultQuestions();
            }
        }
    }

    /**
     * Заполняет список стандартных вопросов (если их нет в базе)
     */
    private function SetDefaultQuestions ()
    {
        $this->questiosList = array();
        $this->questiosList[] = (object) array( "question"   => "2+2*2", 
												"answer"     => "2", 
												"invitation" => "You are welcome!");
    }

    /**
     * Получает список вопросов из базы
     */
    private function GetQuestionsListBromBase ()
    {
        $this->questiosList = array();
        $result = $this->UserProfile->GetBaseLink()->query('SELECT * FROM `' . phpICQ_SQLFieldsNames::antspam_tablename .
         '` WHERE `' . phpICQ_SQLFieldsNames::antspam_userid_fieldname . '` = ' . $this->UserProfile->GetUserProfileID());
        
        while (($row = $result->fetchRow()) == true) {
            $question = (object) array("id" => $row[phpICQ_SQLFieldsNames::antspam_id_fieldname], 
            "question" => $row[phpICQ_SQLFieldsNames::antspam_question_fieldname], 
            "answer" => $row[phpICQ_SQLFieldsNames::antspam_answer_fieldname], 
            "invitation" => $row[phpICQ_SQLFieldsNames::antspam_invitation_fieldname]);
            $this->questiosList[] = $question;
        }
    }

    /**
     * Возвращает объект вопроса и ответа на него если передан ID то получает по номеру
     * 
     * @param mixed $id
     * @return mixed 
     */
    public function GetQuestion ($id = null)
    {
        if (! $this->useAntiSpamFilter) {
            return null;
        }
        
        if ($id == null) {
            return $this->questiosList[rand(0, count($this->questiosList) - 1)];
        } else {
            foreach ($this->questiosList as $question) {
                if ($question->id == $id) {
                    return $question;
                }
            }
            return null;
        }
    }
} // phpICQ_antiSpamFilter



/**
 * Класс вывода информации
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client 
 *
 */
class phpICQ_output
{
    /**
     * @var phpICQ_userProfile
     */
    private $UserProfile;
    /**
     * @var DOMDocument
     */
    private $dom;
    /**
     * @var array
     */
    private $contactFields = array(
            phpICQ_SQLFieldsNames::contact_protocol_fieldname => 'p',
            phpICQ_SQLFieldsNames::contact_uin_fieldname => 'u',
            phpICQ_SQLFieldsNames::contact_status_fieldname => 's',
            phpICQ_SQLFieldsNames::contact_statustext_fieldname=> 'st',
            phpICQ_SQLFieldsNames::contact_incontactlist_fieldname => 'il');
    /**
     * @var array
     */
    private $missMessageFields = array( 
            phpICQ_SQLFieldsNames::messages_message_fieldname, 
            phpICQ_SQLFieldsNames::messages_sendtime_fieldname, 
            phpICQ_SQLFieldsNames::messages_attempts_fieldname, 
            phpICQ_SQLFieldsNames::messages_inout_fieldname, 
            phpICQ_SQLFieldsNames::messages_issend_fieldname, 
            phpICQ_SQLFieldsNames::messages_isdelivered_fieldname, 
            phpICQ_SQLFieldsNames::messages_serverid_fieldname, 
            phpICQ_SQLFieldsNames::messages_userid_fieldname);
    /**
     * @var array
     */
    private $statusesFields = array(
            phpICQ_SQLFieldsNames::statuses_name_fieldname             => 'n',
            phpICQ_SQLFieldsNames::statuses_id_fieldname               => 'i',
            phpICQ_SQLFieldsNames::statuses_alias_fieldname            => 'a',
            phpICQ_SQLFieldsNames::statuses_picture_fieldname          => 'p',
            phpICQ_SQLFieldsNames::statuses_isonline_fieldname         => 'io',
            phpICQ_SQLFieldsNames::statuses_showasuserstatus_fieldname => 's',
            phpICQ_SQLFieldsNames::statuses_protocol_fieldname         => 'pl');                                    

    /**
     * Class constructor
     *
     * @param phpICQ_userProfile $UserProfile
     */
    public function __construct ($UserProfile)
    {
        $this->UserProfile = $UserProfile;
        $this->dom = new DOMDocument('1.0', $UserProfile->baseCharset());
    }

    /**
     * @return DOMDocument
     */
    public function getDOMDocument ()
    {
        return $this->dom;
    }

    /**
     * Список возможных статусов пользователя в XML
     * 
     * @return DOMElement
     */
    public function GetAllStstusesXML ()
    {
        $profile = $this->dom->createElement("statuses");
        
        $allstatuses = $this->UserProfile->GetStatuses()->GetAllStatuses();
        foreach ($allstatuses as $status) {
            $newstatus = $profile->appendChild($this->dom->createElement("s"));
            foreach (array_keys($this->statusesFields) as $field) {
                $newstatus->setAttribute($this->statusesFields[$field], $status[$field]);
            }
        }
        return $profile;
    }

    /**
     * Данные о профиле пользователя в XML
     * 
     * @return DOMElement  
     */
    public function GetUserProfileXML ()
    {
        $profile = $this->dom->createElement("userprofile");
        
        $fields = $this->UserProfile->GetFieldsNames();
        foreach ($fields as $field) {
            // Пропустим если это пароли
            if (($field == phpICQ_SQLFieldsNames::usersprofiles_pass_fieldname) ||
             ($field == phpICQ_SQLFieldsNames::usersprofiles_userpass_fieldname)) {
                continue;
            }
            $profile->setAttribute($field, $this->UserProfile->GetUserProfileFieldValue($field));
        }
        
        $profile->setAttribute("useencoding", ($this->UserProfile->useEncoding()) ? "1" : "0");
        $profile->setAttribute("clientrefreshtime", $this->UserProfile->ClientRefreshTime());
        return $profile;
    }

    /**
     * Контакт в XML
     * 
     * @param phpICQ_contact $contact
     * @return DOMElement
     */
    public function GetContactXML ($contact)
    {
        $XMLcontact = $this->dom->createElement("c");
        $XMLcontact->setAttribute('n', $contact->GetContactName());
        foreach (array_keys($this->contactFields) as $field) {
            $XMLcontact->setAttribute($this->contactFields[$field], $contact->GetContactFieldValue($field));
        } 
        return $XMLcontact;
    }

    /**
     * Список контактов в XML
     * 
     * @param int $filter
     * @param date $modificationTime
     * @return DOMElement  
     */
    public function GetContactListXML ($filter = null, $modificationTime = null)
    {
        $contactlist = $this->UserProfile->usercontactlist->GetContactList($filter, $modificationTime);
        if (count($contactlist) == 0) {
            return null;
        }
        
        $XMLList = $this->dom->createElement("contact_list");
        $XMLList->setAttribute("lm", $this->UserProfile->usercontactlist->GetLastModificationDate());

        foreach ($contactlist as $contact) {
            $XMLList->appendChild($this->GetContactXML($contact));
        }
        return $XMLList;
    }

    /**
     * История контакта в XML
     * 
     * @param DOMDocument $dom
     * @param phpICQ_contact $contact
     * @return DOMElement 
     */
    public function GetContactMessagesHistoryXML ($contact, $last = 5)
    {
        $messageXML = $this->dom->createElement("messages_log");
        $messageXML->setAttribute("uin", $contact->GetContactUIN());
        $messageXML->setAttribute("last", $last);
        
        $contactmess = $contact->GetContactMessages();
        $hist = $contactmess->GetMessageHistory($last);
        
        foreach ($hist as $message) {
            $messageXML->appendChild($this->GetMessageXML($message));
        }
        return $messageXML;
    }

    /**
     * Новые сообщения в XML
     * 
     * @return DOMElement 
     */
    public function GetNewMessagesXML ()
    {
        $messages = phpICQ_messages::GetNewMessages($this->UserProfile);
        if (count($messages) == 0) {
            return null;
        }
        
        $messageXML = $this->dom->createElement("new_messages");
        foreach ($messages as $message) {
            $messageXML->appendChild($this->GetMessageXML($message));
        }
        return $messageXML;
    }

    /**
     * Сообщения в XML
     * 
     * @param array $message
     * @return DOMElement 
     */
    private function GetMessageXML ($message)
    {
        $text = $message[phpICQ_SQLFieldsNames::messages_message_fieldname];
        $text = $this->UserProfile->GetSmiles()->replaceSmiles($text);
        $messageXML = $this->dom->createElement("message", str_replace("&", "", $text));

        $fields = array_keys($message);
        foreach ($fields as $field) {
            if (array_search($field, $this->missMessageFields) !== false) {
                continue;
            }
            
            // Если сообщение исходящее, то в поле UIN запихнем UIN из профиля пользователя
            if (($field == phpICQ_SQLFieldsNames::messages_contactuin_fieldname) &&
             ($message[phpICQ_SQLFieldsNames::messages_inout_fieldname] == phpICQ_messages::OUTBOUND)) {
                $messageXML->setAttribute($field, $this->UserProfile->GetUserProfileUIN());
            } else {
                $messageXML->setAttribute($field, $message[$field]);
            }
        }
        return $messageXML;
    }

    /**
     * Список смайликов для пользователя в XML
     * 
     * @return DOMElement
     */
    public function GetSmilesForUserXML ()
    {
        $SmilesForUser = $this->UserProfile->GetSmiles()->GetSmilesForUser();
        $smilesXML = $this->dom->createElement("smiles");
        $smilesXML->setAttribute("folder", $this->UserProfile->GetSmiles()->GetSmilesFolder());
        foreach ($SmilesForUser as $smile) {
            
            $smileXML = $this->dom->createElement("smile");
            
            $smileXML->setAttribute("str", $smile->smile);
            $smileXML->setAttribute("file", $smile->file);
            $smilesXML->appendChild($smileXML);
        }
        return $smilesXML;
    }
}

/**
 * Класс глобальных настроек и настроек пользователя
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 * 
 */
class phpICQ_settings
{
    
    const TYPE_INT = 1;
    const TYPE_STR = 2;
    const TYPE_BOOL = 3;
    
    /**
     * @var phpICQ_baseConnection
     */
    private $BaseConnection;
    
    /**
     * @var phpICQ_userProfile
     */
    private $userprofile;
    
    /**
     * @var array
     */
    private $settings_values;
    
    /**
     * @var bool
     */
    private $getOnlyGlobalValues;
    
    private $defaultValues = array( "useencoding"             => "0", 
    								"encodingkey"             => "_default_key_", 
    								"clientrefreshtime"       => "5", 
    								"useantispam"             => "1", 
    								"idletime_away"           => "600", 
    								"idletime_na"             => "1800");
    
    private $defaultGlobalSettings = array(
    								"usecompression"          => "1", 
    								"compressionlevel"        => "4", 
    								"usercharset"             => "UTF-8", 
    								"outputcharset"           => "CP1251", 
    								"serverisrun"             => "1", 
    								"timeintervalbeforresend" => "30", 
    								"maxattemptsforsend"      => "10");

    /**
     * Class constructor
     *
     * @param phpICQ_userProfile $userProfile
     */
    function __construct ($userProfile) 
    { 
        $this->BaseConnection = $userProfile->GetBaseLink();
        $this->userprofile = $userProfile;
        $this->getSettingsFromBase();
    }

    /**
     * Читает настройки из базы 
     */
    public function getSettingsFromBase ()
    {
        $this->settings_values = array();
        
        $sql = "SELECT * FROM `" . phpICQ_SQLFieldsNames::settings_tablename . "` " . "WHERE `" . 
               phpICQ_SQLFieldsNames::settings_userid_fieldname ."` = '" . $this->userprofile->GetUserProfileID() . 
               "' OR " . "`" . phpICQ_SQLFieldsNames::settings_userid_fieldname . "` IS NULL"; // Это глобальные значения 
        $result = $this->BaseConnection->query($sql);
        while (($row = $result->fetchRow()) == true) {
            $this->settings_values[strtolower($row[phpICQ_SQLFieldsNames::settings_fields_fieldname])] = $row[phpICQ_SQLFieldsNames::settings_values_fieldname];
        }
    
    }

    private function saveSettingsInBase ($name, $newValue)
    {
        if ($newValue) {
            if (array_key_exists($name, $this->defaultGlobalSettings)) {
                $dopConditions = "`" . phpICQ_SQLFieldsNames::settingsUser_userid_fieldname .
                 "` = NULL";
            } else {
                $dopConditions = "`" . phpICQ_SQLFieldsNames::settingsUser_userid_fieldname .
                 "` = '" . $this->userprofile->GetUserProfileID() . "'";
            }
            
            $sql = "UPDATE `" . phpICQ_SQLFieldsNames::settings_tablename . "` SET `" . phpICQ_SQLFieldsNames::settings_values_fieldname .
                   "` = '" . $this->settings_values[$name] . "' WHERE `" . phpICQ_SQLFieldsNames::settings_fields_fieldname .
                   "` = '" . $name . "' AND " . $dopConditions . " LIMIT 1;";
        } else {
            $sql = "INSERT INTO `" . phpICQ_SQLFieldsNames::settings_tablename . "` (`" . phpICQ_SQLFieldsNames::settings_fields_fieldname .
                   "`, `" . phpICQ_SQLFieldsNames::settings_values_fieldname . "`, `" . phpICQ_SQLFieldsNames::settings_userid_fieldname .
                   "`)" . " VALUES ('" . $name . "', '" . $this->settings_values[$name] . "', " . 
                   ((array_key_exists($name, $this->defaultGlobalSettings)) ? "NULL" : "'" .
             $this->userprofile->GetUserProfileID() . "'") . ")";
        }
        
        $this->BaseConnection->query($sql);
    }

    /**
     * Устанавливает значение настройки
     *
     * @param string $name
     * @param mixed $value
     */
    private function setVelue ($name, $value)
    {
        $settingsName = strtolower($name);
        $isNewValue = array_key_exists($settingsName, $this->settings_values);
        $this->settings_values[$settingsName] = $value;
        $this->saveSettingsInBase($settingsName, $isNewValue);
    }

    /**
     * Возвращает значение настройки
     *
     * @param string $name
     * @return mixed
     */
    private function getVelue ($name)
    {
        $settingsName = strtolower($name);
        
        if (array_key_exists($settingsName, $this->settings_values)) {
            return $this->settings_values[strtolower($name)];
        } else {
            if (array_key_exists($settingsName, $this->defaultValues)) {
                $this->setVelue($settingsName, $this->defaultValues[$settingsName]);
                return $this->defaultValues[$settingsName];
            } else {
                if (array_key_exists($settingsName, $this->defaultGlobalSettings)) {
                    $this->setVelue($settingsName, $this->defaultGlobalSettings[$settingsName]);
                    return $this->defaultGlobalSettings[$settingsName];
                } else {
                    return NULL;
                }
            }
        }
    }

    /**
     * Возвращает/устанавливает значение определенного типа
     * 
     * @param mixed $value
     * @param $type int
     * @return mixed 
     */
    private function valueByType ($name, $value = null, $type = null)
    {
        $val = $this->getVelue($name);
        $valueSet = $value;
        
        switch ($type) {
            case phpICQ_settings::TYPE_BOOL:
                $val = ($val != "0");
                break;
            case phpICQ_settings::TYPE_INT:
                $val = $val + 0;
                break;
        }
        
        if ($value != null) {
            if ($type == phpICQ_settings::TYPE_BOOL) {
                $valueSet = (is_bool($value) ? (($value) ? "1" : "0") : $value);
            }
            $this->setVelue($name, $valueSet);
        }
        
        return $val;
    }

    /**
     * Возвращает/устанавливает использование анти-спам фильтра
     * 
     * @param bool $value
     * @return bool
     */
    public function useAntiSpamFilter ($value = null)
    {
        return $this->valueByType("useantispam", $value, phpICQ_settings::TYPE_BOOL);
    }

    /**
     * Возвращает/устанавливает использование шифрования
     * 
     * @param int $value
     * @return int
     */
    public function useEncoding ($value = null)
    {
        return $this->valueByType("useencoding", $value, phpICQ_settings::TYPE_INT);
    }

    /**
     * Возвращает ключ кодирования
     * 
     * @param string $value
     * @return string
     */
    public function useEncodingKey ($value = null)
    {
        return $this->valueByType("encodingkey", $value, phpICQ_settings::TYPE_STR);
    }

    /**
     * Возвращает время обновления клиента
     * 
     * @param string $value
     * @return string
     */
    public function ClientRefreshTime ($value = null)
    {
        return $this->valueByType("clientrefreshtime", $value, phpICQ_settings::TYPE_INT);
    }

    /**
     * Искользовать сжатие gzip
     * 
     * @return bool
     */
    public function useCompression ($value = null)
    {
        return $this->valueByType("usecompression", $value, phpICQ_settings::TYPE_BOOL);
    }

    /**
     * Степень сжатия при использовании gzip'a
     * 
     * @return int
     */
    public function useCompressionLevel ($value = null)
    {
        return $this->valueByType("compressionlevel", $value, phpICQ_settings::TYPE_INT);
    }

    /**
     * Возвращает/устанавливает статус агента сбора/отправки сообщений
     * 
     * @param bool $value
     * @return bool
     */
    public function serverStatus ($value = null)
    {
        return $this->valueByType("serverisrun", $value, phpICQ_settings::TYPE_BOOL);
    }

    /**
     * Время ожидания для повторной отправки сообщения
     * 
     * @return int
     */
    public function timeIntervalBeforReSend ($value = null)
    {
        return $this->valueByType("timeintervalbeforresend", $value, phpICQ_settings::TYPE_INT);
    }

    /**
     * Максимальное число попыток для отправки
     * 
     * @return int
     */
    public function maxAttemptsForSend ($value = null)
    {
        return $this->valueByType("maxattemptsforsend", $value, phpICQ_settings::TYPE_INT);
    }

    /**
     * @return string
     */
    public function baseCharset ($value = null)
    {
        return $this->valueByType("usercharset", $value, phpICQ_settings::TYPE_STR);
    }

    /**
     * @return string
     */
    public function outputCharSet ($value = null)
    {
        return $this->valueByType("outputcharset", $value, phpICQ_settings::TYPE_STR);
    }

    /**
     * Возвращает время для автоматического перехода на статус Away 
     * 
     * @return int
     */
    public function idleTimeAway ($value = null)
    {
        return $this->valueByType("idletime_away", $value, phpICQ_settings::TYPE_INT);
    }

    /**
     * Возвращает время для автоматического перехода на статус NA 
     * 
     * @return int
     */
    public function idleTimeNA ($value = null)
    {
        return $this->valueByType("idletime_na", $value, phpICQ_settings::TYPE_INT);
    }
    
    /**
     * @param phpICQ_baseConnection $BaseConnection
     * @param bool $value
     */
    public static function ServerStatusBase ($BaseConnection, $value = null) {
        $sql = "SELECT * FROM `" . phpICQ_SQLFieldsNames::settings_tablename . "` WHERE `" .
               phpICQ_SQLFieldsNames::settings_fields_fieldname . "` = 'serverisrun'" .
               " AND `" . phpICQ_SQLFieldsNames::settings_userid_fieldname . "` IS NULL"; // Это глобальные значения
         
        //echo $sql;
        $result = $BaseConnection->query($sql);
        
        //var_dump($result);
        
        $row = $result->fetchRow();
        $oldValue = null;
        if ($row !== null) {
            $oldValue = ($row[phpICQ_SQLFieldsNames::settings_fields_fieldname] != "0"); 
        } else {
            $oldValue = true;
        }
        
        $valueStr = (is_bool($value) ? (($value) ? "1" : "0"): $value);
        
        if ($value !== null) { // Че-т установили обратно
            if ($row !== null) { // UPDATE
                $sql = "UPDATE ". phpICQ_SQLFieldsNames::settings_tablename ." SET `".phpICQ_SQLFieldsNames::settings_fields_fieldname."` = '".$valueStr.
	                "' WHERE `" . phpICQ_SQLFieldsNames::settings_userid_fieldname . "` IS NULL AND `".
	                phpICQ_SQLFieldsNames::settings_fields_fieldname . "` = 'serverisrun'";
                $result = $BaseConnection->query($sql);
            } else { // INCERT
                echo "ERROR!!\n";
            }
        }
        return $oldValue;
    }

} // phpICQ_settings

/**
 * Класс работы со статистикой
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client 
 *
 */
class phpICQ_statistics
{
    
    /**
     * @var phpICQ_baseConnection
     */
    private $BaseConnection;
    
    /**
     * @var phpICQ_userProfile
     */
    private $userprofile;
    
    private $userid;
    private $in;
    private $out;
    private $lastactivity;
    private $idleTime;
    
    /**
     * @var Log
     */
    private $logger;

    /**
     * Class constructor
     *
     * @param phpICQ_userProfile $userprofile
     * @param string $uin
     */
    function __construct ($userprofile)
    { // Constructor
        $this->userprofile = $userprofile;
        $this->userid = $this->userprofile->userid;
        
        $this->BaseConnection = $this->userprofile->GetBaseLink();
        $this->logger = $this->BaseConnection->GetLoger('stat');
        
        $this->GetStatisticsFromBase();
    }

    public function GetStatisticsFromBase ()
    {
        $fields = "*, NOW()-`" . phpICQ_SQLFieldsNames::statistics_lastactivity_fieldname . "` AS notupdated";
        $sql = "SELECT " . $fields . " FROM `" . phpICQ_SQLFieldsNames::statistics_tablename . "` WHERE `" . phpICQ_SQLFieldsNames::statistics_userid_fieldname .
         "` = " . $this->userid;
        $result = $this->BaseConnection->query($sql);
        $row = $result->fetchRow();
        
        if ($row === null) { // Нужно создать такую запись
            $this->BaseConnection->query("INSERT INTO `" . phpICQ_SQLFieldsNames::statistics_tablename .
             "` (`" . phpICQ_SQLFieldsNames::statistics_userid_fieldname . "`) VALUES ('" . $this->userid .
             "')");
            $this->in = 0;
            $this->out = 0;
            $this->lastactivity = null;
            $this->idleTime = 0;
        } else {
            $this->in = $row[phpICQ_SQLFieldsNames::statistics_in_fieldname];
            $this->out = $row[phpICQ_SQLFieldsNames::statistics_out_fieldname];
            $this->lastactivity = $row[phpICQ_SQLFieldsNames::statistics_lastactivity_fieldname];
            $this->idleTime = $row["notupdated"];
        }
    }

    private function AddTraffic ($count, $field)
    {
        $querysCount = $this->BaseConnection->GetQuerysCount() + 1;
        $this->BaseConnection->query("UPDATE `" . phpICQ_SQLFieldsNames::statistics_tablename . "` SET `" . $field . "` = `" .
         $field . "` + '" . $count . "' " . ", `" . phpICQ_SQLFieldsNames::statistics_lastactivity_fieldname . "` = NOW() " .
         ", `" . phpICQ_SQLFieldsNames::statistics_requests_fieldname . "` = `" . phpICQ_SQLFieldsNames::statistics_requests_fieldname .
         "` + 1 " . ", `" . phpICQ_SQLFieldsNames::statistics_querys_fieldname . "` = `" . phpICQ_SQLFieldsNames::statistics_querys_fieldname .
         "` +  " . $querysCount . " WHERE `" . phpICQ_SQLFieldsNames::statistics_userid_fieldname . "` = " . $this->userid .
         " LIMIT 1;");
    }

    public function AddInTraffic ($count)
    {
        $this->in += $count;
        $this->AddTraffic($count, phpICQ_SQLFieldsNames::statistics_in_fieldname);
    }

    public function AddOutTraffic ($count)
    {
        $this->out += $count;
        $this->AddTraffic($count, phpICQ_SQLFieldsNames::statistics_out_fieldname);
    }

    /**
     * @return int
     */
    public function GetIdleTime ()
    {
        return $this->idleTime + 0;
    }
} // phpICQ_statistics



/**
 * Основной класс, профиль пользователя
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client 
 *
 */
class phpICQ_userProfile extends phpICQ_settings
{
    /**
     * @var phpICQ_baseConnection
     */
    private $BaseConnection;
    /**
     * @var phpICQ_statuses
     */
    private $statuses;
    /**
     * @var phpICQ_contactList
     */
    public $usercontactlist;
    /**
     * @var phpICQ_statistics
     */
    private $statistics;
    /**
     * phpICQ_smiles
     */
    private $smiles;
    /**
     * @var phpICQ_antiSpamFilter
     */
    private $antiSpam;
    /**
     * @var Log
     */
    public $logger;
    /**
     * @var DOMDocument
     */
    private $dom;
    /**
     * @var int
     */
    public $userid;
    /**
     * @var bool
     */
    private $autosave = true;
    /**
     * @var array
     */
    private $fields;
    /**
     * @var array
     */
    private $oldfields;
    /**
     * @var array
     */
    private $fieldsnames;

    /**
     * Class constructor
     *
     * @param string $ID
     * @param phpICQ_baseConnection $BaseConnection
     */
    function __construct ($ID, $BaseConnection) // Constructor 
    {
        $this->fieldsnames = phpICQ_SQLFieldsNames::GetUserProfileFields();
        $this->oldfields = null;
        
        $this->BaseConnection = $BaseConnection;
        $this->userid = $ID;
        $this->logger = $this->BaseConnection->GetLoger('profile');
        $this->GetProfileInformation();
        parent::__construct($this);
        
        $this->statuses = new phpICQ_statuses($this);
        $this->outputClass = new phpICQ_output($this);
        $this->statistics = new phpICQ_statistics($this);
        $this->usercontactlist = new phpICQ_contactList($this);
        $this->smiles = new phpICQ_smiles($this);
        $this->antiSpam = new phpICQ_antiSpamFilter($this);
    }

    /**
     * @return phpICQ_settings
     */
    public function GetSettings ()
    {
        return $this->settings;
    }

    /**
     * @return phpICQ_statistics
     */
    public function GetStatistics ()
    {
        return $this->statistics;
    }

    /**
     * @return phpICQ_baseConnection
     */
    public function GetBaseLink ()
    {
        return $this->BaseConnection;
    }

    /**
     * @return phpICQ_output
     */
    public function GetOutputClass ()
    {
        return $this->outputClass;
    }

    /**
     * @return phpICQ_statuses
     */
    public function GetStatuses ()
    {
        return $this->statuses;
    }

    /**
     * @return phpICQ_smiles
     */
    public function GetSmiles ()
    {
        return $this->smiles;
    }

    /**
     * Присваеваеи значение полю контакта
     * 
     * @param string $fieldname
     * @param string $value
     */
    public function SetUserProfileFieldValue ($fieldname, $value)
    {
        if (array_key_exists($fieldname, $this->fields)) {
            if ($fieldname == phpICQ_SQLFieldsNames::usersprofiles_userpass_fieldname) {
                $this->fields[$fieldname] = md5($value);
            } else {
                $this->fields[$fieldname] = $value;
            }
        }
        
        if ($this->autosave) {
            $this->SaveUserProfileInBase();
        }
    }

    /**
     * Возвращает значение поля контакта
     * 
     * @return string
     */
    public function GetUserProfileFieldValue ($fieldname)
    {
        return (array_key_exists($fieldname, $this->fields)) ? $this->fields[$fieldname] : NULL;
    }

    /**
     * Возвращает старое значение поля контакта
     * 
     * @return string
     */
    public function GetUserProfileOldFieldValue ($fieldname)
    {
        return (array_key_exists($fieldname, $this->oldfields)) ? $this->oldfields[$fieldname] : NULL;
    }

    /**
     * getting user information from BD
     * 
     * @return boolean;
     */
    private function GetProfileInformation ()
    {
        $sql = "SELECT * FROM `" . phpICQ_SQLFieldsNames::usersprofiles_tablename . "` WHERE (" . phpICQ_SQLFieldsNames::usersprofiles_id_fieldname .
         "=" . $this->userid . ")";
        $result = $this->BaseConnection->Query($sql);
        //$num_rows = $this->BaseConnection->num_rows ( $result );
        $num_rows = $result->numRows();
        
        if ($num_rows == 0) {
            die("User with ID=$this->userid not found!");
        } else {
            //$row = $this->BaseConnection->fetch_array ( $result );
            $row = $result->fetchRow();
            if ($row === null) {
                die("error while getting user info!");
            }
        }
        
        $firstrefresh = ($this->oldfields == null);
        
        foreach ($this->fieldsnames as $fieldname) {
            $value = ((array_key_exists($fieldname, $row)) ? $row[$fieldname] : NULL);
            if ($firstrefresh) {
                $this->oldfields[$fieldname] = $value;
            } else {
                $this->oldfields[$fieldname] = $this->fields[$fieldname];
            }
            $this->fields[$fieldname] = $value;
        }
        
        return true;
    }

    /**
     * Возвращает ID пользователя по логину и паролю или false если нет такого 
     * 
     * @param phpICQ_baseConnection $BaseConnection
     * @param string $UserName
     * @param string $UserProfilePass
     * @return int
     */
    static function GetContactByNamePass ($BaseConnection, $UserName, $UserProfilePass)
    {
        $UserProfilePassMD5 = md5($UserProfilePass);
        
        $sql = 'SELECT `' . phpICQ_SQLFieldsNames::usersprofiles_id_fieldname . '` FROM `users` WHERE `' . phpICQ_SQLFieldsNames::usersprofiles_name_fieldname .
         '` = "' . $UserName . '" AND `' . phpICQ_SQLFieldsNames::usersprofiles_userpass_fieldname . '` = "' . $UserProfilePassMD5 .
         '"';
        
        $result = $BaseConnection->Query($sql);
        //$num_rows = $BaseConnection->num_rows($result);
        $num_rows = $result->numRows();
        if ($num_rows == 1) {
            //$row = $BaseConnection->fetch_array($result);
            $row = $result->fetchRow();
            return $row[phpICQ_SQLFieldsNames::usersprofiles_id_fieldname];
        }
        
        return false;
    }

    /**
     * Создает новый профайл в базе и возвращает ID этого профайла
     *  
     * @var phpICQ_baseConnection $BaseConnection
     * @var String $UserName
     * @var String $UserProfilePass
     * @var String $UserUIN
     * @var String $UserPass
     * @return String
     */
    static function CreateNewUserProfile ($BaseConnection, $UserName = "", $UserProfilePass = "", $UserUIN = "", $UserPass = "")
    {
        
        $fields_str = '`' . phpICQ_SQLFieldsNames::usersprofiles_name_fieldname . '`, ' . '`' . phpICQ_SQLFieldsNames::usersprofiles_userpass_fieldname .
         '`, ' . '`' . phpICQ_SQLFieldsNames::usersprofiles_uin_fieldname . '`, ' . '`' . phpICQ_SQLFieldsNames::usersprofiles_pass_fieldname .
         '` ';
        
        $UserProfilePassMD5 = md5($UserProfilePass);
        $data_str = '"' . $UserName . '", "' . $UserProfilePassMD5 . '", "' . $UserUIN . '", "' . $UserPass . '" ';
        $sql = 'INSERT INTO `' . phpICQ_SQLFieldsNames::usersprofiles_tablename . '` (' . $fields_str . ') VALUES (' . $data_str .
         ');';
        $sql2 = 'SELECT `' . phpICQ_SQLFieldsNames::usersprofiles_id_fieldname . '` FROM `users` ORDER BY `' . phpICQ_SQLFieldsNames::usersprofiles_id_fieldname .
         '` DESC LIMIT 1';
        
        $result = $BaseConnection->Query($sql);
        if (! $result) {
            die("Error creating new User profile!");
        }
        
        //$row = $BaseConnection->fetch_array($BaseConnection->Query ( $sql2 ));
        $row = $BaseConnection->Query($sql2)->fetchRow();
        
        if ($row === null) {
            die("Error geting user profile ID");
        }
        
        return $row[phpICQ_SQLFieldsNames::usersprofiles_id_fieldname];
    }

    /**
     * Сохраняет контакт в базе данных
     * 
     * @return boolean
     */
    public function SaveUserProfileInBase ()
    {
        $fieldnames = $this->BaseConnection->GetTableFieldsNames(phpICQ_SQLFieldsNames::usersprofiles_tablename);
        $fields_str = "";
        
        foreach ($fieldnames as $fildfrondb) {
            if ($fildfrondb == phpICQ_SQLFieldsNames::usersprofiles_id_fieldname) {
                continue;
            }
            if (! empty($this->fields[$fildfrondb])) {
                $fields_str = $fields_str . '`' . $fildfrondb . '` = "' . $this->fields[$fildfrondb] .
                 '", ';
            }
        }
        
        $fields_str = rtrim($fields_str, ", ");
        $sql = 'UPDATE `' . phpICQ_SQLFieldsNames::usersprofiles_tablename . '` SET ' . $fields_str . ' WHERE `' . phpICQ_SQLFieldsNames::usersprofiles_id_fieldname .
         '` = ' . $this->userid . ' LIMIT 1';
        
        $result = $this->BaseConnection->Query($sql);
        if ($result) {
            return true;
        } else {
            return false; // Error creating contact
        }
    }

    /**
     * Возвращает массив имен полей профайла
     * 
     * @return array
     */
    public function GetFieldsNames ()
    {
        return $this->fieldsnames;
    }

    /**
     * Обновляет информацию о профайле из базы
     */
    public function RefreshProfileInformation ()
    {
        $this->GetProfileInformation();
        parent::getSettingsFromBase();
    }

    /**
     * @return boolean
     */
    public function IsStatusChanged ()
    {
        return (($this->GetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_status_fieldname) != $this->GetUserProfileOldFieldValue(phpICQ_SQLFieldsNames::usersprofiles_status_fieldname)) ||
         ($this->GetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_statustext_fieldname) != $this->GetUserProfileOldFieldValue(phpICQ_SQLFieldsNames::usersprofiles_statustext_fieldname)));
    }

    /**
     * @return boolean
     */
    public function IsXStatusChanged ()
    {
        return ($this->GetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_xstatus_fieldname) != $this->GetUserProfileOldFieldValue(phpICQ_SQLFieldsNames::usersprofiles_xstatus_fieldname));
    }

    /**
     * Подтверждает доставку сообщений пользователю или от пользователя 
     * @param int/array $messagesIDs
     */
    public function ConfirmMessageDelivery ($messagesIDs, $isServerIDs = false)
    {
        return phpICQ_messages::ConfirmMessageDelivery($this, $messagesIDs, $isServerIDs);
    }

    /**
     * Подтверждает отправку сообщений контакту 
     * @param int/array $messagesIDs
     */
    public function ConfirmMessageSend ($messagesID, $messageServerID)
    {
        return phpICQ_messages::ConfirmMessageSend($this, $messagesID, $messageServerID);
    }

    /**
     * @return array
     */
    public function GetMessagesForSend ()
    {
        return phpICQ_messages::GetMessagesForSend($this);
    }

    /**
     * @return int
     */
    public function GetMessageStatus ($messageid)
    {
        return phpICQ_messages::GetMessageStatus($this, $messageid);
    }

    /**
     * @return array
     */
    public function GetNewMessages ()
    {
        return phpICQ_messages::GetNewMessages($this);
    }

    /**
     * @return string
     */
    public function GetUserProfileUIN ()
    {
        return $this->GetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_uin_fieldname);
    }

    /**
     * @return string
     */
    public function GetUserProfileUINPass ()
    {
        return $this->GetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_pass_fieldname);
    }

    /**
     * @return string
     */
    public function GetUserProfileID ()
    {
        return $this->GetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_id_fieldname);
    }

    /**
     * @return string
     */
    public function GetUserProfileStatus ()
    {
        return $this->GetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_status_fieldname);
    }

    /**
     * @return string
     */
    public function GetUserProfileStatusText ()
    {
        return $this->GetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_statustext_fieldname);
    }

    /**
     * @return string
     */
    public function GetUserProfileXStatus ()
    {
        return $this->GetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_xstatus_fieldname);
    }

    public function SetUserProfileName ($name)
    {
        $this->SetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_name_fieldname, $name);
    }

    public function SetUserProfileStatus ($status)
    {
        $this->SetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_status_fieldname, $status);
    }

    public function SetUserProfileUserPass ($userpass)
    {
        $this->SetUserProfileFieldValue(phpICQ_SQLFieldsNames::usersprofiles_userpass_fieldname, $userpass);
    }
} // phpICQ_userProfile



/**
 * Clsss phpICQ_contact
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 * 
 */
class phpICQ_contact
{
    /**
     * @var phpICQ_baseConnection
     */
    private $BaseConnection;
    /**
     * @var bool
     */
    private $autosave = true;
    /**
     * @var bool
     */
    private $modified = false;
    private $newcontact;
    /**
     * User info fields
     * 
     * @var array
     */
    private $fields;
    private $fieldsnames;
    /**
     * Current user ID
     * 
     * @var int
     */
    private $userid;
    /**
     * @var phpICQ_userProfile
     */
    private $userprofile;
    /**
     * @var phpICQ_messages
     */
    private $contactmessages;

    /**
     * Class constructor
     *
     * @param phpICQ_userProfile $userprofile
     * @param string $uin
     */
    function __construct ($userprofile, $fields)
    {
        $this->fieldsnames = phpICQ_SQLFieldsNames::GetContactFields();
        $this->userprofile = &$userprofile;
        $this->userid = &$this->userprofile->userid;
        $this->BaseConnection = $this->userprofile->GetBaseLink();
        
        // Creating fields array
        $this->fields = Array();
        foreach ($this->fieldsnames as $fieldname) {
            $this->fields[$fieldname] = "";
        }
        
        if (is_array($fields)) { // Если передан массив, то бум заполнять из него (ничего из базы не читаем)
            $this->newcontact = false;
            $this->modified = false;
            foreach ($this->fieldsnames as $fieldname) {
                $this->fields[$fieldname] = ((array_key_exists($fieldname, $fields)) ? $fields[$fieldname] : NULL);
            }
        } else {
            $this->fields[phpICQ_SQLFieldsNames::contact_uin_fieldname] = $fields;
            
            // Geting user information from DB
            $this->GetUserInformation();
        }
        
        // Creating messages class
        $this->contactmessages = new phpICQ_messages($userprofile, $this);
    }

    /**
     * Class destructor
     *
     */
    function __destruct ()
    {
        if (($this->autosave) && ($this->modified)) {
            $this->SaveContactInBase();
        }
    }

    /**
     * @return boolean
     */
    public function IsNewContact ()
    {
        return $this->newcontact;
    }

    /**
     * @return boolean
     */
    public function IsAutosave ()
    {
        return $this->autosave;
    }

    /**
     * @param $autosave boolean
     */
    public function SetAutosave ($autosave)
    {
        $this->autosave = $autosave;
    }

    /**
     * @return boolean
     */
    public function IsDeleted ()
    {
        $deleted = $this->GetContactFieldValue(phpICQ_SQLFieldsNames::contact_deleted_fieldname);
        return ($deleted == NULL) ? false : $deleted;
    }

    /**
     * Возвращает кодировку сообщений пользователя
     * 
     * @return string
     */
    public function GetContactCharset ()
    {
        return $this->GetContactFieldValue(phpICQ_SQLFieldsNames::contact_usercharset_fieldname);
    }

    /**
     * Сообщения контакта
     * 
     * @return phpICQ_messages
     */
    public function GetContactMessages ()
    {
        return $this->contactmessages;
    }

    public function SetContactFieldValue ($fieldname, $value)
    {
        if (array_key_exists($fieldname, $this->fields)) {
            if ($this->fields[$fieldname] == $value) { // Ничего не меняем, т.к. значения одинаковые
                return;
            }
            $this->fields[$fieldname] = $value;
        }
        
        $this->modified = true;
        
        if ($this->autosave) {
            $this->SaveContactInBase();
        }
    }

    /**
     * Возвращает значение поля контакта
     * 
     * @return string
     */
    public function GetContactFieldValue ($fieldname)
    {
        return (array_key_exists($fieldname, $this->fields)) ? $this->fields[$fieldname] : NULL;
    }

    private function GetContactFromDB ()
    {
        $uin = $this->fields[phpICQ_SQLFieldsNames::contact_uin_fieldname];
        $sql = "SELECT * FROM `" . phpICQ_SQLFieldsNames::contact_tablename . "` WHERE (" . phpICQ_SQLFieldsNames::contact_userid_fieldname .
         "=" . $this->userid . ")" . (empty($uin) ? "" : " and (" . phpICQ_SQLFieldsNames::contact_uin_fieldname . ' = "' .
         $uin . '" )');
        
        return $this->BaseConnection->Query($sql);
    }

    /**
     * getting user information from BD
     * 
     * @return boolean;
     */
    private function GetUserInformation ()
    {
        $uin = $this->fields[phpICQ_SQLFieldsNames::contact_uin_fieldname];
        
        $result = $this->GetContactFromDB();
        $num_rows = $result->numRows();
        if ($num_rows != 1) { // Нашли больше 1 такого юзера
            if ($num_rows == 0) {
                $this->newcontact = true;
                $this->modified = true;
                if ($this->autosave) {
                    $this->SaveContactInBase();
                    $this->newcontact = false;
                    $result = $this->GetContactFromDB();
                    $row = $result->fetchRow();
                    if ($row === null) {
                        $this->userprofile->logger->err("error while getting contcat info!");
                        die("error while getting contcat info!");
                    }
                }
            } else {
                die("dublicate user UID! (" . $uin . ")" . $num_rows);
            }
        } else {
            $this->newcontact = false;
            $this->modified = false;
            $row = $result->fetchRow();
            if ($row === null) {
                $this->userprofile->logger->err("error while getting contcat info!");
                die("error while getting contcat info!");
            }
        }
        
        foreach ($this->fieldsnames as $fieldname) {
            if (! $this->newcontact) {
                $this->fields[$fieldname] = ((array_key_exists($fieldname, $row)) ? $row[$fieldname] : NULL);
            } else {
                $this->fields[phpICQ_SQLFieldsNames::contact_uin_fieldname] = NULL;
            
            }
        }
        
        $this->fields[phpICQ_SQLFieldsNames::contact_uin_fieldname] = $uin;
        return true;
    }

    /**
     * Сохраняет контакт в базе данных
     * 
     * @return boolean
     */
    public function SaveContactInBase ()
    {
        $uin = $this->fields[phpICQ_SQLFieldsNames::contact_uin_fieldname];
        if ($uin != NULL) {
            $fieldnames = $this->BaseConnection->GetTableFieldsNames(phpICQ_SQLFieldsNames::contact_tablename);
            $fields_str = "";
            $data_str = "";
            
            foreach ($fieldnames as $fildfrondb) {
                // Пропустип поля с ID и changed (они заполняются автоматически)
                if (($fildfrondb ==
                 phpICQ_SQLFieldsNames::contact_id_fieldname) || ($fildfrondb == phpICQ_SQLFieldsNames::contact_changed_fieldname)) {
                    continue;
                }
                if (! empty($this->fields[$fildfrondb])) {
                    if ($this->newcontact) {
                        $fields_str = $fields_str . '`' . $fildfrondb . '`, ';
                        $data_str = $data_str . '"' . $this->fields[$fildfrondb] . '", ';
                    } else {
                        $fields_str = $fields_str . '`' . $fildfrondb . '` = "' . $this->fields[$fildfrondb] . '", ';
                    }
                }
            }
            
            if ($this->newcontact) {
                $fields_str = $fields_str . '`' . phpICQ_SQLFieldsNames::contact_userid_fieldname .
                 '`, `' . phpICQ_SQLFieldsNames::contact_changed_fieldname . '`';
                $data_str = $data_str . '"' . $this->userid . '", NOW()';
                $sql = 'INSERT INTO `' . phpICQ_SQLFieldsNames::contact_tablename . '` (' .
                 $fields_str . ') VALUES (' . $data_str . ')';
            } else {
                $fields_str = rtrim($fields_str, ", ");
                $sql = 'UPDATE `' . phpICQ_SQLFieldsNames::contact_tablename . '` SET ' .
                 $fields_str . ', `' . phpICQ_SQLFieldsNames::contact_changed_fieldname .
                 '` = NOW()' . ' WHERE `' . phpICQ_SQLFieldsNames::contact_id_fieldname .
                 '` = ' . $this->GetContactFieldValue(phpICQ_SQLFieldsNames::contact_id_fieldname) .
                 ' LIMIT 1';
            }
            
            $result = $this->BaseConnection->Query($sql);
            if ($result) {
                $this->newcontact = false; // Это уже палюбому не новый контакт
                $this->modified = false;
                return true;
            } else {
                return false; // Error creating contact
            }
        } else {
            return false; // Error contact exists
        }
    }

    /**
     * Удаляет контакт из БД
     * 
     * @return boolean 
     */
    public function RemoveContactFromBase ()
    {
        $result = $this->contactmessages->RemoveAllMessagesFromDB();
        if (! $result) {
            return false;
        }
        
        $uin = $this->GetContactFieldValue(phpICQ_SQLFieldsNames::contact_uin_fieldname);
        
        $sql = 'DELETE FROM `' . phpICQ_SQLFieldsNames::contact_tablename . '` WHERE `' . phpICQ_SQLFieldsNames::contact_userid_fieldname .
         '` = "' . $this->userid . '" AND `' . phpICQ_SQLFieldsNames::contact_uin_fieldname . '` = "' . $uin . '" LIMIT 1';
        $result = $this->BaseConnection->query($sql);
        if ($result) {
            unset($this);
            return true;
        } else {
            return false;
        }
    }

    /**
     * Помечает контакт удаленным
     * 
     * @return boolean 
     */
    public function DeleteContact ()
    {
        $this->SetContactFieldValue(phpICQ_SQLFieldsNames::contact_deleted_fieldname, true);
        if (! $this->autosave) { // Если не стоит признак автоудаления, то сохраним контакт
            $this->SaveContactInBase();
        }
    }

    /**
     * Возвращает массив имен полей профайла
     * 
     * @return array
     */
    public function GetFieldsNames ()
    {
        return $this->fieldsnames;
    }

    /**
     * @return string
     */
    public function GetContactName ()
    {
        $displayName = $this->GetContactFieldValue(phpICQ_SQLFieldsNames::contact_displayname_fieldname);
        $name = $this->GetContactFieldValue(phpICQ_SQLFieldsNames::contact_name_fieldname);
        return (empty($displayName)) ? ((empty($name)) ? $this->GetContactUIN() : $name) : $displayName;
    }

    /**
     * @return string
     */
    public function GetContactUIN ()
    {
        return $this->GetContactFieldValue(phpICQ_SQLFieldsNames::contact_uin_fieldname);
    }

    /**
     * @return string
     */
    public function GetContactID ()
    {
        return $this->GetContactFieldValue(phpICQ_SQLFieldsNames::contact_id_fieldname);
    }

    public function GetContactStatus ()
    {
        return $this->GetContactFieldValue(phpICQ_SQLFieldsNames::contact_status_fieldname);
    }

    public function SetContactName ($name)
    {
        $this->SetContactFieldValue(phpICQ_SQLFieldsNames::contact_name_fieldname, $name);
    }

    public function SetContactStatus ($status)
    {
        $this->SetContactFieldValue(phpICQ_SQLFieldsNames::contact_status_fieldname, $status);
    }

    public function SetContactGroupID ($GroupID)
    {
        $this->SetContactFieldValue(phpICQ_SQLFieldsNames::contact_groupid_fieldname, $GroupID);
    }

    public function SetContactServerID ($ServerID)
    {
        $this->SetContactFieldValue(phpICQ_SQLFieldsNames::contact_serverid_fieldname, $ServerID);
    }

    public function SetContactInList ($InList)
    {
        $this->SetContactFieldValue(phpICQ_SQLFieldsNames::contact_incontactlist_fieldname, (is_bool($InList) ? (($InList) ? "1" : "0") : $InList));
    }
} // phpICQ_contact



/**
 * Класс контакт листа
 * 
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 * 
 */
class phpICQ_contactList
{
    // Режимы сортировки
    const ORDER_BY_NAME = 0x0001;
    const ORDER_BY_UIN = 0x0002;
    const ORDER_BY_STATUS = 0x0010;
    
    // Фильтры для получения контактлиста
    const CONTACTLIST_ALL_CONTACTS = 0x0001;
    const CONTACTLIST_ONLY_ONLINE = 0x0002;
    const CONTACTLIST_ONLY_OFFLINE = 0x0003;
    
    /**
     * @var phpICQ_baseConnection
     */
    private $BaseConnection;
    
    /**
     * Массив контакт листа
     * 
     * @var array
     */
    private $ContactList;
    
    private $userid;
    
    /**
     * @var phpICQ_userProfile
     */
    private $userprofile;
    
    /**
     * Contact list sort mode
     * 
     * @var int
     */
    private $sortmode = phpICQ_contactList::ORDER_BY_STATUS;
    
    /**
     * @var Class_Output
     */
    private $output;

    /**
     * Class constructor
     *
     * @param phpICQ_userProfile $userprofile
     */
    function __construct ($userprofile)
    {
        $this->userprofile = &$userprofile;
        $this->userid = &$this->userprofile->userid;
        $this->BaseConnection = $this->userprofile->GetBaseLink();
        $this->sortmode = phpICQ_contactList::ORDER_BY_STATUS | phpICQ_contactList::ORDER_BY_NAME;
    }

    public function SetAllContactsOffLineStatus ()
    {
        $sql = 'UPDATE `' . phpICQ_SQLFieldsNames::contact_tablename . '` SET `' . phpICQ_SQLFieldsNames::contact_status_fieldname .
         '` = "" ' . 'WHERE `' . phpICQ_SQLFieldsNames::contact_userid_fieldname . '` =' . $this->userid;
        $this->BaseConnection->Query($sql);
    }

    public function GetLastModificationDate ()
    {
        //SELECT max(`changed`) as change_date FROM `contactlist` WHERE `userid` = 1
        $sql = 'SELECT max(`' . phpICQ_SQLFieldsNames::contact_changed_fieldname .
         '`) as change_date FROM `' . phpICQ_SQLFieldsNames::contact_tablename . '` WHERE (' . phpICQ_SQLFieldsNames::contact_userid_fieldname .
         '=' . $this->userid . ') ';
        $result = $this->BaseConnection->Query($sql);
        //$row = $this->BaseConnection->fetch_array ( $result );
        $row = $result->fetchRow();
        
        if ($row !== null) {
            return $row['change_date'];
        } else {
            return null;
        }
    }

    /**
     * получает контакт лист из БД
     * 
     * @return array
     */
    public function GetContactList ($filter = null, $modificationTime = null)
    {
        $SQLFilter = "";
        if (($filter != null) && ($filter != phpICQ_contactList::CONTACTLIST_ALL_CONTACTS)) {
            $SQLFilter = "AND `" . phpICQ_SQLFieldsNames::contact_status_fieldname . '` ' . (($filter ==
             phpICQ_contactList::CONTACTLIST_ONLY_ONLINE) ? 'NOT' : '') . ' IN ("' . phpICQ_statuses::STATUS_OFFLINE .
             '", "") ';
        }
        if ($modificationTime != null) {
            $SQLFilter = $SQLFilter . "AND (`" . phpICQ_SQLFieldsNames::contact_changed_fieldname . "` > '" .
             $modificationTime . "') ";
        }
        
        // Только которые в контакт-листе на сервере, либо прошли спам фильтр (если он включен)...
        $SQLFilter .= 'AND ((`' . phpICQ_SQLFieldsNames::contact_incontactlist_fieldname .
         '` = 1) OR (' . '`' . phpICQ_SQLFieldsNames::contact_notaspam_fieldname . '` = 1))';
        
        $sql = 'SELECT * FROM `' . phpICQ_SQLFieldsNames::contact_tablename . '` WHERE (' . phpICQ_SQLFieldsNames::contact_userid_fieldname .
         '=' . $this->userid . ') ' . $SQLFilter . $this->GetSQLSortMode();
        
        $result = $this->BaseConnection->Query($sql);
        
        $this->ContactList = Array();
        while (($row = $result->fetchRow()) == true) {
            // Заполняем все необходимые поля контакта из выбранного массива (так должно быть быстрее чем по одному) 
            $NewContact = new phpICQ_contact($this->userprofile, $row);
            //$NewContact = new phpICQ_contact ( $this->userprofile, $row ['uin'] );
            $this->ContactList[$NewContact->GetContactFieldValue(phpICQ_SQLFieldsNames::contact_uin_fieldname)] = $NewContact;
        }
        
        return $this->ContactList;
    }

    /**
     * Количество контактов
     * 
     * @return int 
     */
    public function GetCount ()
    {
        $sql = 'SELECT COUNT(*) AS CONTNUM FROM `' . phpICQ_SQLFieldsNames::contact_tablename . '` WHERE ' . phpICQ_SQLFieldsNames::contact_userid_fieldname .
         '=' . $this->userid;
        $result = $this->BaseConnection->Query($sql);
        //$row = $this->BaseConnection->fetch_array($result);
        $row = $result->fetchRow();
        
        if ($row === null) {
            return 0;
        } else {
            return $row['CONTNUM'];
        }
    }

    /**
     * Получает контакт по его UIN'у
     *
     * @return phpICQ_contact
     */
    public function GetContactByUID ($uin)
    {
        return new phpICQ_contact($this->userprofile, $uin);
    }

    /**
     * Режим сортировки для БД
     *  
     * @return string; 
     */
    private function GetSQLSortMode ()
    {
        $Order = "";
        
        if ($this->sortmode & phpICQ_contactList::ORDER_BY_STATUS) {
            $Order = $Order . phpICQ_SQLFieldsNames::contact_status_fieldname . " DESC, ";
        }
        
        if ($this->sortmode & phpICQ_contactList::ORDER_BY_NAME) {
            $Order = $Order . phpICQ_SQLFieldsNames::contact_name_fieldname . ", ";
        } elseif ($this->sortmode & phpICQ_contactList::ORDER_BY_UIN) {
            $Order = $Order . phpICQ_SQLFieldsNames::contact_uin_fieldname . ", ";
        }
        
        $Order = rtrim($Order, ", ");
        return (empty($Order) ? "" : " ORDER BY " . $Order);
    }

    public function SetSortMode ($SortMode)
    {
        $this->sortmode = $SortMode;
    }

    /**
     * Возвращает текущий режим сортировки
     * 
     * @return  int
     */
    public function GetSortMode ()
    {
        return $this->sortmode;
    }
}


/**
 * Класс отправки/получения сообщений...
 *  
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 */
class phpICQ_messages
{
    const INBOUND = 1;
    const OUTBOUND = 0;
    const DELIVERED = 1;
    const NOT_DELIVERED = 0;
    const SEND = 1;
    const NOT_SEND = 0;
    
    /**
     * @var phpICQ_baseConnection
     */
    private $BaseConnection;
    
    # contact list info
    private $userid;
    
    /**
     * @var phpICQ_contact
     */
    private $contact;
    private $contactuid;
    
    private $base_charset;

    /**
     * Class constructor
     *
     * @param phpICQ_userProfile $userprofile
     * @param phpICQ_contact $contact
     */
    function __construct ($userprofile, $contact)
    {
        //$this->userprofile = $userprofile;
        $this->BaseConnection = $userprofile->GetBaseLink();
        $this->userid = &$userprofile->userid;
        $this->contact = &$contact;
        $this->base_charset = $userprofile->baseCharset();
        $this->contactuid = $this->contact->GetContactUIN();
    }

    /**
     * Создает текст запроса с ограничениями из массива
     * 
     * @param array $filterarray
     * @return string
     */
    static function CreateMessageQuery ($userid, $filterarray)
    {
        $sql = 'SELECT * FROM `' . phpICQ_SQLFieldsNames::messages_tablename . '` WHERE (' . '`' . phpICQ_SQLFieldsNames::messages_userid_fieldname .
         '` ="' . $userid . '") ';
        
        if (count($filterarray) != 0) {
            $sql .= "AND";
            $fields = array_keys($filterarray);
            $where = '';
            foreach ($fields as $field) {
                $where .= ' (`' . $field . '` = "' . $filterarray[$field] . '") AND';
            }
            $sql .= rtrim($where, "AND");
        }
        return $sql;
    }

    /**
     * Добавляет новое сообщение и возвращает его ID
     * 
     * @return string
     */
    public function AddMessage ($message, $date = "", $inout = 1)
    {
        $message = addslashes($message);
        
        $sql = 'INSERT INTO `' . phpICQ_SQLFieldsNames::messages_tablename . '` (`' . phpICQ_SQLFieldsNames::messages_userid_fieldname .
         '`, `' . phpICQ_SQLFieldsNames::messages_date_fieldname . '`, `' . phpICQ_SQLFieldsNames::messages_inout_fieldname .
         '`, `' . phpICQ_SQLFieldsNames::messages_contactuin_fieldname . '`, `' . phpICQ_SQLFieldsNames::messages_message_fieldname .
         '`, `' . phpICQ_SQLFieldsNames::messages_isdelivered_fieldname . '`, `' . phpICQ_SQLFieldsNames::messages_issend_fieldname .
         '`)' . 'VALUES ("' . $this->userid . '", ' . (empty($date) ? "NOW()" : $date) . ', "' . $inout . '", "' . $this->contactuid .
         '", "' . $message . '", "' . phpICQ_messages::NOT_DELIVERED . '", "' . phpICQ_messages::NOT_SEND . '")';
        $result = $this->BaseConnection->Query($sql);
        
        $sql = 'SELECT `' . phpICQ_SQLFieldsNames::messages_id_fieldname . '` FROM `' . phpICQ_SQLFieldsNames::messages_tablename .
         '` ORDER BY `' . phpICQ_SQLFieldsNames::messages_id_fieldname . '` DESC LIMIT 1';
        $result = $this->BaseConnection->Query($sql);
        //$row = $this->BaseConnection->fetch_array($result);
        $row = $result->fetchRow();
        
        if ($row === null) {
            return false;
        } else {
            return $row[phpICQ_SQLFieldsNames::messages_id_fieldname];
        }
    }

    /**
     * Возвращает массив сообщений из истории
     * 
     * @param int $last
     * @return array
     */
    public function GetMessageHistory ($last = 5)
    {
        $filter = array(phpICQ_SQLFieldsNames::messages_isdelivered_fieldname => phpICQ_messages::DELIVERED, 
        phpICQ_SQLFieldsNames::messages_contactuin_fieldname => $this->contactuid);
        $sql = $this->CreateMessageQuery($this->userid, $filter);
        $sql .= 'ORDER BY `' . phpICQ_SQLFieldsNames::messages_date_fieldname . '` DESC' . (($last != - 1) ? ' LIMIT ' .
         $last : '');
        
        $result = $this->BaseConnection->query($sql);
        $mess = array();
        //$userUIN = $this->
        while (($row = $result->fetchRow()) == true) {
            $mess[] = $row;
        }
        
        return $mess;
    }

    /**
     * Подтверждает доставку сообщения
     *
     * @param phpICQ_userProfile $userprofile
     * @param int/array $messagesIDs
     * @return boolean
     */
    static public function ConfirmMessageDelivery ($userprofile, $messagesIDs, $isServerIDs = false)
    {
        $baseconnection = $userprofile->GetBaseLink();
        
        $messagesIDstr = "";
        if (is_array($messagesIDs)) {
            $messagesIDstr = implode(",", $messagesIDs);
        } else {
            $messagesIDstr = $messagesIDs;
        }
        //UPDATE `messages` SET `confrimed` = '1' WHERE `id` IN (25, 27, 28, 29)
        $sql = 'UPDATE `' . phpICQ_SQLFieldsNames::messages_tablename . '` SET `' .
         phpICQ_SQLFieldsNames::messages_isdelivered_fieldname . '` = "' . phpICQ_messages::DELIVERED . '" WHERE `' . (($isServerIDs) ? phpICQ_SQLFieldsNames::messages_serverid_fieldname : phpICQ_SQLFieldsNames::messages_id_fieldname) .
         '` IN (' . $messagesIDstr . ')';
        
        $baseconnection->Query($sql);
        return $baseconnection->affectedRows();
    }

    /**
     * Подтверждает отправку сообщения
     *
     * @param phpICQ_userProfile $userprofile
     * @param int/array $messagesIDs
     * @return boolean
     */
    static public function ConfirmMessageSend ($userprofile, $messagesID, $messagesServerID)
    {
        $baseconnection = $userprofile->GetBaseLink();
        
        $sql = 'UPDATE `' . phpICQ_SQLFieldsNames::messages_tablename . '` SET `' . phpICQ_SQLFieldsNames::messages_issend_fieldname .
         '` = "' . phpICQ_messages::SEND . '", ' . '`' . phpICQ_SQLFieldsNames::messages_serverid_fieldname . '` = "' .
         $messagesServerID . '", ' . '`' . phpICQ_SQLFieldsNames::messages_sendtime_fieldname . '` = NOW(), ' . '`' . phpICQ_SQLFieldsNames::messages_attempts_fieldname .
         '` = `' . phpICQ_SQLFieldsNames::messages_attempts_fieldname . '` + 1' . ' WHERE `' . phpICQ_SQLFieldsNames::messages_id_fieldname .
         '` = ' . $messagesID;
        $baseconnection->Query($sql);
        return $baseconnection->affectedRows();
    }

    /**
     * Возвращает сообщения для отправки
     * 
     * @param phpICQ_userProfile $userprofile
     * @return array
     */
    static public function GetMessagesForSend ($userprofile)
    {
        $baseconnection = $userprofile->GetBaseLink();
        $userid = $userprofile->userid;
        
        $sql = 'SELECT * FROM `' . phpICQ_SQLFieldsNames::messages_tablename . '` WHERE ' . '`' . phpICQ_SQLFieldsNames::messages_userid_fieldname .
         '` ="' . $userid . '" AND ' . '`' . phpICQ_SQLFieldsNames::messages_inout_fieldname . '` = "' . phpICQ_messages::OUTBOUND .
         '" AND (' . '`' . phpICQ_SQLFieldsNames::messages_issend_fieldname . '` = "' . phpICQ_messages::NOT_SEND . '" OR (' .
         '`' . phpICQ_SQLFieldsNames::messages_isdelivered_fieldname . '` = "' . phpICQ_messages::NOT_DELIVERED . '" AND (' .
         'round( NOW( ) - `' . phpICQ_SQLFieldsNames::messages_sendtime_fieldname . '`) > ' . $userprofile->timeIntervalBeforReSend() .
         ' AND ' . '`' . phpICQ_SQLFieldsNames::messages_attempts_fieldname . '` < ' . $userprofile->maxAttemptsForSend() .
         ')))';
        
        $result = $baseconnection->Query($sql);
        $messages = array();
        
        if ($result) {
            while (($row = $result->fetchRow()) == true) {
                $messages[] = array(
                phpICQ_SQLFieldsNames::messages_id_fieldname => $row[phpICQ_SQLFieldsNames::messages_id_fieldname], 
                phpICQ_SQLFieldsNames::messages_contactuin_fieldname => $row[phpICQ_SQLFieldsNames::messages_contactuin_fieldname], 
                phpICQ_SQLFieldsNames::messages_message_fieldname => $row[phpICQ_SQLFieldsNames::messages_message_fieldname]);
            }
        }
        return $messages;
    }

    /**
     * Возвращает все новые сообщения
     * 
     * @param phpICQ_userProfile $userprofile
     * @return array
     */
    static public function GetNewMessages ($userprofile)
    {
        $baseconnection = $userprofile->GetBaseLink();
        $userid = $userprofile->userid;
        
        $filter = array(phpICQ_SQLFieldsNames::messages_inout_fieldname => phpICQ_messages::INBOUND, 
        phpICQ_SQLFieldsNames::messages_isdelivered_fieldname => phpICQ_messages::NOT_DELIVERED);
        $sql = phpICQ_messages::CreateMessageQuery($userid, $filter);
        $sql .= 'ORDER BY `' . phpICQ_SQLFieldsNames::messages_date_fieldname . '` ASC ';
        $result = $baseconnection->query($sql);
        $mess = array();
        while (($row = $result->fetchRow()) == true) {
            $mess[] = $row;
        }
        return $mess;
    }

    /**
     * Созвращает статус сообщения
     * 
     * @param phpICQ_userProfile $userprofile
     * @param int $messageid
     * @return int or false
     */
    static public function GetMessageStatus ($userprofile, $messageid)
    {
        $baseconnection = $userprofile->GetBaseLink();
        $userid = $userprofile->userid;
        
        $sql = 'SELECT ' . phpICQ_SQLFieldsNames::messages_isdelivered_fieldname . ',' . phpICQ_SQLFieldsNames::messages_issend_fieldname .
         ' FROM `' . phpICQ_SQLFieldsNames::messages_tablename . '` WHERE ' . phpICQ_SQLFieldsNames::messages_userid_fieldname .
         ' ="' . $userid . '" AND ' . phpICQ_SQLFieldsNames::messages_id_fieldname . ' = "' . $messageid . '"';
        
        $result = $baseconnection->Query($sql);
        if (! $result) {
            return false;
        } else {
            //$row = $baseconnection->fetch_array ( $result );
            $row = $result->fetchRow();
            if ($row === null) {
                return false;
            } else {
                return $row[phpICQ_SQLFieldsNames::messages_isdelivered_fieldname];
            }
        }
    }

    /**
     * Удаляет все сообщения контакта из БД
     * 
     * @return boolean
     */
    public function RemoveAllMessagesFromDB ()
    {
        $sql = 'DELETE FROM `' . phpICQ_SQLFieldsNames::messages_tablename . '` WHERE ' . phpICQ_SQLFieldsNames::messages_userid_fieldname .
         ' ="' . $this->userid . '" ' . 'AND ' . phpICQ_SQLFieldsNames::messages_contactuin_fieldname . ' = "' . $this->contactuid .
         '"';
        
        $result = $this->BaseConnection->Query($sql);
        return $result;
    }
}

/**
 * Класс обмена инфомацией в XML формате
 *
 * @author Alexey MAGician Sergeev
 * @version 1.0.0
 * @package phpICQ_Client
 * 
 */
class phpICQ_ajax
{
    /**
     * @var phpICQ_baseConnection 
     */
    private $BaseConnection;
    /**
     * @var bool 
     */
    private $documentEmpty = true;
    /**
     * @var DOMDocument
     */
    private $dom;
    /**
     * @var DOMElement
     */
    private $rootElement;
    /**
     * @var phpICQ_userProfile
     */
    private $UserProfile;
    /**
     * @var phpICQ_output 
     */
    private $UserProfileOutputClass;
    /**
     * @var Log
     */
    private $ajaxLog; 

    /**
     * Constructor
     *
     * @param phpICQ_userProfile $UserProfile
     */
    function __construct ($UserProfile)
    {
        $this->UserProfile = $UserProfile;
        $this->BaseConnection = $UserProfile->GetBaseLink();
        $this->ajaxLog = $this->BaseConnection->GetLoger("ajax");
        
        $this->UserProfileOutputClass = $this->UserProfile->GetOutputClass();
        $this->dom = $this->UserProfileOutputClass->getDOMDocument();
        
        // Если не используем шифрование и сжание, то бум форматировать выходные данные (больше передаваемый объем инфы)
        if (($UserProfile->useEncoding() == 0) && (! $UserProfile->useCompression())) {
            $this->dom->formatOutput = true;
        }
        
        $this->rootElement = $this->dom->createElement("data");
        $this->rootElement->setAttribute("client_version", "1.0.0");
        $this->rootElement->setAttribute("generate_time", $this->GetTime());
        $this->dom->appendChild($this->rootElement);
        
        //$this->ajaxLog->log(print_r($_REQUEST, true));
        
        $actionsStr = isset($_REQUEST["actions"]) ? $_REQUEST["actions"] : null;
        //$this->rootElement->setAttribute("actions", $actionsStr);
        
        // Если передано несколько действий, то бум делать все по порядку
        $actionsArray = explode(",", $actionsStr);
        $i = 0;
        foreach ($actionsArray as $currentAcrion) {
            // Полученный блок XML по данной операции, либо null если не повезло =)
            $actionXML = $this->GetActionXML($currentAcrion, $this->GetActionParams($i));
            $i ++;
            
            if ($actionXML != null) {
                //$actionXML->setAttribute("action", $currentAcrion);
                $this->rootElement->appendChild($actionXML);
                $this->documentEmpty = false;
            }
        }
    }

    /**
     * @return array
     */
    private function GetActionParams ($actionNum)
    {
        $allkeys = array_keys($_REQUEST);
        $params = Array();
        
        foreach ($allkeys as $currentKey) {
            if (strpos($currentKey, "P" . $actionNum . "__") === 0) {
                $params[substr($currentKey, strlen("P" . $actionNum . "__"))] = $_REQUEST[$currentKey];
            }
        }
        return $params;
    }

    /**
     * Возвращает XML сделанной операции
     * 
     * @param array $action
     * @return DOMElement
     */
    private function GetActionXML ($action, $params)
    {
        switch ($action) {
            case "test":
                return $this->dom->createElement('test', 'test ajax from php');
                break;
            case "GetUserContactList":
                
                $dopParam = null;
                if (array_key_exists("lm", $params)) {
                    $dopParam = $params["lm"];
                }
                
                $XML = $this->UserProfileOutputClass->GetContactListXML(null, $dopParam);
                if (($dopParam != null)&&($XML != null)) {
                    //$XML->setAttribute("from_modified", $dopParam);
                }
                return $XML;
                // phpICQ_contactList::CONTACTLIST_ONLY_ONLINE
                break;
            case "GetAllStatuses":
                return $this->UserProfileOutputClass->GetAllStstusesXML();
                break;
            case "GetNewMessages":
                return $this->UserProfileOutputClass->GetNewMessagesXML();
                break;
            case "newMessagesForSend": // TODO Кое как вроде отправляются, но надо будет еще позырить по подробнее на эту тему...
                $messCount = $params["messCount"];
                $messCount = 0 + $messCount; // Преобразование строки в число
                if (is_int($messCount)) {
                    $messagesXML = $this->dom->createElement("messages");
                    for ($i = 0; $i < $messCount; $i ++) {
                        $contactUIN = $params["mesfor_$i"];
                        $contactMessage = $params["mestext_$i"];
                        
                        $Contact = $this->UserProfile->usercontactlist->GetContactByUID($contactUIN);
                        $MessageID = $Contact->GetContactMessages()->AddMessage($contactMessage, "", phpICQ_messages::OUTBOUND);
                        
                        $messageXML = $this->dom->createElement("message");
                        $messageXML->setAttribute("number", $i);
                        $messageXML->setAttribute("contactUIN", $contactUIN);
                        $messageXML->setAttribute("id", ($MessageID ===
                         false) ? - 1 : $MessageID);
                        $messagesXML->appendChild($messageXML);
                    }
                    return $messagesXML;
                }
                
                return $messagesXML;
                break;
            case "newDeliveredMessages":
                $MessagesIDs = $params["MessagesIDs"];
                $mesNum = $this->UserProfile->ConfirmMessageDelivery($MessagesIDs);
                $newDeliveredMessages = $this->dom->createElement("newDeliveredMessages");
                $newDeliveredMessages->setAttribute("num", $mesNum);
                
                return $newDeliveredMessages;
                break;
            case "getContactHistory":
                $contactUIN = $params["contactUIN"];
                $last = isset($params["last"]) ? $params["last"] : 5;
                
                $Contact = $this->UserProfile->usercontactlist->GetContactByUID($contactUIN);
                $LogmessagesXML = $this->UserProfileOutputClass->GetContactMessagesHistoryXML($Contact, $last);
                
                return $LogmessagesXML;
                break;
            case "getUserProfileInfo":
                return $this->UserProfileOutputClass->GetUserProfileXML();
                break;
            case "getSmilesForUser":
                return $this->UserProfileOutputClass->GetSmilesForUserXML();
                break;
            default:
                //$this->rootElement->setAttribute("error", "error_action");
                return null;
                break;
        }
    
    }

    /**
     * Какой тип сжатия поддерживает браузер
     * 
     * @return mixed
     */
    private function CheckCanGzip ()
    {
        if (headers_sent() || connection_aborted()) {
            return null;
        }
        if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false)
            return "x-gzip";
        if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false)
            return "gzip";
        return null;
    }

    
    /**
     * @return string
     */
    private function GetTime ()
    {
        return date(DATE_ISO8601, time());
    }

    /**
     * @return string
     */
    public function GetResult ()
    {
        $this->rootElement->setAttribute('q', $this->BaseConnection->GetQuerysCount());
        return $this->dom->saveXML();
    }

    public function OutputResult ()
    {
        //flush();
        if ($this->documentEmpty) {
            return null;
        }
        
        $result = $this->GetResult();
        $useEncoding = $this->UserProfile->useEncoding();
        $useEncodingKey = $this->UserProfile->useEncodingKey();
        
        if ($useEncoding == 0) {
            header('Content-type: application/xml');
        }
        
        // Решим что нам делать со сжатием
        $canUseGZip = $this->CheckCanGzip();
        $useCompression = (($canUseGZip != null) && ($this->UserProfile->useCompression()));
        if ($useCompression) {
            header("Content-Encoding: $canUseGZip");
        }
        
        if ($useEncoding == 1) {
            $result = base64_encode($result);
        } elseif ($useEncoding >= 2) {
            // Нифига не оптимальное шифрование из-за долбаной Кирилицы!
            $result = $result . "#" . crc32($result);
            $result = base64_encode($result);
            $rc4 = new Crypt_RC4($useEncodingKey);
            $rc4->crypt($result);
            $result = base64_encode($result);
        }

        // Если бум сжимать то сшимаем gzip
        if ($useCompression) {
            $result = gzencode($result, $this->UserProfile->useCompressionLevel());
        }
        $this->UserProfile->GetStatistics()->AddInTraffic(strlen($result));
        echo $result;
    }
} // phpICQ_ajax

?>
